#ifndef ____BLOCKQUEUE_HPP____
#define ____BLOCKQUEUE_HPP____

#include <iostream>
#include <queue>
#include <pthread.h>
#include<string>
#include<unistd.h>
#include"Task.hpp"

const int Capacity = 5;

template <class T>
class BlockQueue
{
private:
    bool empty()
    {
        return _bq.empty();
    }

    bool full()
    {
        return _bq.size() == _cap;
    }

public:
    BlockQueue(int cap = Capacity)
        : _cap(cap)
    {
        pthread_mutex_init(&_lock, nullptr);
        pthread_cond_init(&_c_cond, nullptr);
        pthread_cond_init(&_p_cond, nullptr);
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&_lock);
        pthread_cond_destroy(&_c_cond);
        pthread_cond_destroy(&_p_cond);

    }

    void push(const T &_data)
    {
        {
            pthread_mutex_lock(&_lock);
            while(full())
            {
                _p_wait++;
                pthread_cond_wait(&_p_cond, &_lock);
                _p_wait--;
            }

            _bq.push(_data);
            if(_c_wait > 0)
                pthread_cond_signal(&_c_cond);
            pthread_mutex_unlock(&_lock);
        }
    }


    T pop()
    {
        pthread_mutex_lock(&_lock);
        while(empty())
        {
            _c_wait++;
            pthread_cond_wait(&_c_cond,&_lock);
            _c_wait--;
        }

        T _data = _bq.front();
        _bq.pop();
        if(_p_wait > 0)
            pthread_cond_signal(&_p_cond);
        pthread_mutex_unlock(&_lock);

        return _data; 
    }

private:
    std::queue<T> _bq;

    size_t _cap;
    pthread_mutex_t _lock;
    pthread_cond_t _c_cond;
    pthread_cond_t _p_cond;
    size_t _c_wait = 0;
    size_t _p_wait = 0;
};

struct data
{
    BlockQueue<Task>* _bq;
    std::string _name;
};

#endif


